#include <strmif.h>
#include "mkvstreamaudioac3.hpp"
#include <cassert>
#ifdef _DEBUG
#include <odbgstream.hpp>
#include <iomanip>
using std::endl;
using std::hex;
using std::dec;
using std::setfill;
using std::setw;
#endif

//Audio Substream Headers
//http://www.mpucoder.com/DVD/ass-hdr.html
//
//AC3 frames are 32ms in length, or 2880 ticks of the 90KHz clock. The resulting 
//frame rate is 31.25 fps.
//
//AC3 (Dolby Digital) Headers
//http://www.mpucoder.com/DVD/ac3hdr.html
//
//Digital Audio Compression (AC-3, E-AC3) Standard, Rev. B
//http://www.atsc.org/standards/a_52b.pdf
//
//AC-3 Compressed Audio (Dolby Digital), Revision A:
//http://www.digitalpreservation.gov/formats/fdd/fdd000209.shtml
//
//http://web.archive.org/web/20060207093602/http://www.sparta.lu.se/~bjorn/whitney/references/ac3spec.pdf
//Chapter 5 (p. 12) describes Sync Frame.

namespace MkvMux
{


MkvStreamAudioAc3::Frame::Frame(Ac3Parser::Frame* f) :
   m_pFrame(f)
{
}


MkvStreamAudioAc3::Frame::~Frame()
{
   Ac3Parser::Frame::Destroy(m_pFrame);
}


ULONG MkvStreamAudioAc3::Frame::curr_timecode_ms() const
{
   return m_pFrame->timecode_ms();
}


ULONG MkvStreamAudioAc3::Frame::size() const
{
   return m_pFrame->buflen();
}


const BYTE* MkvStreamAudioAc3::Frame::data() const
{
   return m_pFrame->buf();
}


MkvStreamAudioAc3::MkvStreamAudioAc3(
    MkvContext& c,
    const BYTE* pb,
    ULONG cb) :
    MkvStreamAudio(c, pb, cb)
{
}


void MkvStreamAudioAc3::WriteTrackName()
{
    EbmlIO::File& f = m_context.m_file;

    f.WriteID2(0x536E);   //name
    f.Write1UTF8(L"AC-3 audio stream");
}


void MkvStreamAudioAc3::WriteTrackCodecID()
{
    EbmlIO::File& f = m_context.m_file;

    f.WriteID1(0x86);  //Codec ID
    f.Write1String("A_AC3");    //TODO

    //TODO:    
    //http://www.matroska.org/technical/specs/codecid/index.html
    //
    //BSID <= 8 !! The private data is void ??? 
    //  Corresponding ACM wFormatTag : 0x2000 ; channel number have to be read 
    //  from the corresponding audio element.
    //
    //A_AC3/BSID9
    //A_AC3/BSID10 
    //
    //AC3/BSID9 and AC3/BSID10 (DolbyNet) : 
    //The ac3 frame header has, similar to the mpeg-audio header a version field. 
    //Normal ac3 is defined as bitstream id 8 (5 Bits, numbers are 0-15). 
    //Everything below 8 is still compatible with all decoders that handle 8 
    //correctly. Everything higher are additions that break decoder compatibility.
    //For the samplerates 24kHz (00); 22,05kHz (01) and 16kHz (10) the BSID is 9.
    //For the samplerates 12kHz (00); 11,025kHz (01) and 8kHz (10) the BSID is 10.
    //END TODO    
}


void MkvStreamAudioAc3::WriteTrackCodecName()
{
    EbmlIO::File& f = m_context.m_file;

    f.WriteID3(0x258688);  //Codec Name
    f.Write1UTF8(L"AC-3");
}



HRESULT MkvStreamAudioAc3::Write(IMediaSample* pSample)
{
   assert(pSample);

   BYTE* ptr;

   HRESULT hr = pSample->GetPointer(&ptr);
   assert(SUCCEEDED(hr));
   assert(ptr);
   hr;
         
   const long len = pSample->GetActualDataLength();
   assert(len >= 0);

   m_parser.Enqueue(ptr, len);

   while (Ac3Parser::Frame* f = m_parser.Dequeue())
   {
      //We might have to create a generic "audio frame" class
      //or just "frame" class containing audio-specific things,
      //generic so that it can be shared among all possible
      //audio input streams.  Here the Ac3Parser::Frame type
      //could just derive from this abstract base class.
      //We could use refcounting the same as we do for 
      //dshow types.

      //Alternatively, at the point where we make the
      //decision to write the frame (decide which cluster),
      //we could just guery the stream an opaque way, 
      //such as "what is your earliest time", and "write
      //a frame now".  So the context would talke to the
      //stream, instead of dealing with frames directly.
      //This has the benefit of isolating parser details
      //(e.g. Ac3Parser::Frame) for a stream from muxer details.

      Frame* const pFrame = new (std::nothrow) Frame(f);
      assert(pFrame);  //TODO

      m_context.WriteAudio(this, pFrame);
   }
        
   return S_OK;
}


int MkvStreamAudioAc3::EndOfStream()
{
   m_parser.EndOfStream();

   while (Ac3Parser::Frame* f = m_parser.Dequeue())
   {
      Frame* const pFrame = new (std::nothrow) Frame(f);
      assert(pFrame);  //TODO

      m_context.WriteAudio(this, pFrame);
   }
   
   return m_context.EOSAudio(this);
}


}  //end namespace MkvMux
